import java.io.*;
//import java.net.*;
import javax.swing.*;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

public class MainServer {

	public static final String SHORT_FILE_NAME = "tempTransferShort.txt";
	public static final String LONG_FILE_NAME = "tempTransferLong.txt";
	public static final int SHORT_FILE_MEGABYTES = 1;
	public static final int LONG_FILE_MEGABYTES = 100;
	public static final int SERVER_SOCKET_PORT = 9876;
	

	public static void main(String[] args) throws IOException{
		//Generate Test Files. Terminate if unsuccessful
		if(!generateTestFiles()){
			System.exit(1);
		}
		
		if(!printStartInfo()){
			System.exit(1);
		}	
		
		Socket serverSocket = generateServerSocket();
		DataInputStream socketIn = new DataInputStream(serverSocket.getInputStream());
		DataOutputStream socketOut = new DataOutputStream(serverSocket.getOutputStream());
		
		boolean quit = false;
		while(!quit)
		{		
			char command = receiveCommand(socketIn);
			quit = executeCommand(command, socketOut);
		} 
		try {
			socketIn.close();
			socketOut.close();
			serverSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		JOptionPane.showMessageDialog(null, "Closing Server!");
	}

	public static boolean generateTestFiles(){
		File shortFile = new File (SHORT_FILE_NAME);
		File longFile = new File (LONG_FILE_NAME);
		
		boolean isGenerated = false;
		if(shortFile.exists())
			isGenerated = true;
		else
			isGenerated = populateFile(shortFile, SHORT_FILE_MEGABYTES);	//Populate the shortFile with SHORT_FILE_MEGABYTES MegaBytes
		
		if(!isGenerated)	//Ensures that if the shortFile generation failed, the method immediately returns, indicating this failure.
			return false;
		
		if (longFile.exists())
			isGenerated = true;
		else 
			isGenerated = populateFile(longFile, LONG_FILE_MEGABYTES);	//Populate the longFile with LONG_FILE_MEGABYTES MegaBytes
	
		return isGenerated;
	}
	
	public static boolean printStartInfo(){
		JOptionPane.showMessageDialog(null, "Welcome to the Server Application!\n This App Uses Port " + SERVER_SOCKET_PORT + ".\nPlease Make Sure This Port is Open");
		
		String ipServer = "Can not access IP";
		InetAddress ip = getLocalIP();
		if(ip != null){
			ipServer = ip.getHostAddress();
			JOptionPane.showMessageDialog(null, "Your IP is "+ipServer+"\n Press Ok to Wait For Client Connection...");
			return true;
		}	
		else {
			return false;
		}
	}	
	
	public static Socket generateServerSocket(){
		ServerSocket welcomeSocket = null;
		Socket serverSocket = null;
		try {
			welcomeSocket = new ServerSocket(SERVER_SOCKET_PORT);
			serverSocket = welcomeSocket.accept();
			welcomeSocket.close();
		} catch (IOException e) {
			try {
				welcomeSocket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			JOptionPane.showMessageDialog(null, "The Socket for the client could not be created.");
			e.printStackTrace();
		}
		return serverSocket;
	}
	
	
	//Receive the packet from the client that specifies which test is to be run, long or short, or if the program should terminate.
		public static char receiveCommand(DataInputStream socketIn) {
			
			try{
				return socketIn.readChar();
			} catch(IOException e){	
				JOptionPane.showMessageDialog(null, "The command was not successfully received from the client.");
				e.printStackTrace();
			}
			return 'E';
		}

		
	public static boolean executeCommand(char command, DataOutputStream socketOut) {
		switch (command) {
		case 'S':
			sendFile(SHORT_FILE_NAME, socketOut);
			break;

		case 'L':
			sendFile(LONG_FILE_NAME, socketOut);
			break;
		case 'C':
			String customFileName = getFileNameFromUser();
			sendFile(customFileName, socketOut);
			break;

		case 'E':
			System.out.println("Exiting Server");
			return true;

		default:
			System.out.println("Invalid Command: " + command);
			break;
		}
		
		return false;
	}
	
	private static String getFileNameFromUser() {
		final JFrame frame = new JFrame("Choose a file to send.");
		final JFileChooser fc = new JFileChooser();
		fc.setMultiSelectionEnabled(false);
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		int val = fc.showOpenDialog(frame);
		return fc.getSelectedFile().getAbsolutePath();
	}

	/** populateFile is a method that generates a file filed with ones, in byte form. 
	 * 	The file size will be numOfMegaBytes megabytes large.
	 * @param fileToPopulate	The File to be populated. Assumes file is empty.
	 * @param numOfMegaBytes	The file size in number of megabytes.
	 * @return	True if the file was successfully populated, false otherwise.
	 */
	private static boolean populateFile(File fileToPopulate, int numOfMegaBytes)
	{
		byte megaByteOfData[] = new byte[1048576];	//Create an array holding 1 MB of data. (1048576 Bytes = 1 MB)
		for(int i = 0; i < 1048576; i++)	// 1048576 Bytes = 1 MB
			megaByteOfData[i] = 1;
		
		FileOutputStream fosShort = null;
  		boolean isFileCreated = false;	//Keeps track of if the file has been successfully populated.
		try{
			fosShort = new FileOutputStream(fileToPopulate);
			for (int i = 0; i < numOfMegaBytes; i++)	// Write the specified number of MB.
				fosShort.write(megaByteOfData);
			isFileCreated = true;
		}catch (IOException ioe) {
			JOptionPane.showMessageDialog(null, "The file " + fileToPopulate.getName() + " could not be populated.");
			isFileCreated = false;
		}
		finally {	//If the file can now be closed, everything is right with the world.
			try {
				fosShort.close();
				
			} catch (IOException io) {
				JOptionPane.showMessageDialog(null, "The file " + fileToPopulate.getName() + " could not be populated.");
				isFileCreated = false;
			}
		}
		return isFileCreated;
	}
	
	private static InetAddress getLocalIP(){
		InetAddress ip = null;
		try {
			ip = InetAddress.getLocalHost();
		} catch (UnknownHostException e) { 
			JOptionPane.showMessageDialog(null, "UnknownHostException in method getLocalIP.");
		}
		return ip;
	}
	
	
	

	public static boolean sendFile(String filename, DataOutputStream socketOut) {
		FileInputStream fileStream = null;
		System.out.println("Starting send of " + filename);
		
		try {
			File sendFile = new File(filename);
			fileStream = new FileInputStream(sendFile);
			socketOut.writeLong(sendFile.length());
			byte[] buffer = new byte[1024];
			long numberOfBytesSent = 0;
			long bytesJustRead = 0;
			while((bytesJustRead = fileStream.read(buffer)) != -1)
			{
				socketOut.write(buffer);
				numberOfBytesSent += bytesJustRead;
			}
			System.out.println("Sent " + numberOfBytesSent + " bytes.");
			System.out.println("Done.");
			fileStream.close();
			return true;
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(null, "The file named '" + filename + "' could not be sent because it could not be found.");
			e.printStackTrace();
		} catch (IOException e){
			JOptionPane.showMessageDialog(null, "The file could not be read.");
			e.printStackTrace();
		}finally{
			try{
				fileStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return false;
	}
}
